package UI;

import java.awt.Font;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.sql.SQLException;
import java.util.ArrayList;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.BevelBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableColumn;

import threads.BaseRunnableBCBS;
import Logic.Account;
import Logic.SQLReader;
import UI.BaseModels.BCBSPanel;
import UI.BaseModels.HeaderLabel;
import UI.BaseModels.ReturnToAdminMenuButton;
import UI.TableModels.TableModelUsers;

import javax.swing.JTextField;

public class ListUsersPanel extends BCBSPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1178586649495176672L;
	private JTable userTable;
	private TableModelUsers tablemodel;
	private JScrollPane scrollpane;
	private JButton loadMoreButton;
	private JLabel lblUserId;
	private JButton btnRefresh;
	private JTextPane txtpnTextFromTransaction;
	private String searchTerm;
	private Account selectedUser;

	private int gotUsersUntil;
	private JLabel lblSearch;
	private JTextField searchField;
	private JButton getUserButton;

	public ListUsersPanel() {
		HeaderLabel header = new HeaderLabel("User List");
		this.add(header);

		ReturnToAdminMenuButton rtmm = new ReturnToAdminMenuButton();
		this.add(rtmm);

		userTable = new JTable();
		userTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		userTable.setRowSelectionAllowed(true);
		userTable.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null,
				null, null));
		tablemodel = new TableModelUsers();
		userTable.setModel(tablemodel);

		userTable.setBounds(0, 51, 305, 230);
		userTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
		userTable.setTableHeader(null);
		userTable.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				int row = userTable.getSelectedRow();
				if (row != -1) {
					selectUser(row);
				}
			}
		});

		TableColumn col1 = userTable.getColumnModel().getColumn(0);
		TableColumn col2 = userTable.getColumnModel().getColumn(1);
		TableColumn col3 = userTable.getColumnModel().getColumn(2);

		col1.setPreferredWidth(10);
		col2.setPreferredWidth(155);
		col3.setPreferredWidth(90);

		scrollpane = new JScrollPane(userTable);
		scrollpane.setViewportBorder(null);
		scrollpane
				.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

		scrollpane.setBounds(0, 51, 305, 139);
		scrollpane.setVisible(true);
		this.add(scrollpane);

		JLabel lblUser = new JLabel("ID");
		lblUser.setFont(new Font("Tahoma", Font.BOLD, 11));
		lblUser.setBounds(7, 32, 26, 14);
		this.add(lblUser);

		JLabel lblAmount = new JLabel("Username");
		lblAmount.setFont(new Font("Tahoma", Font.BOLD, 11));
		lblAmount.setBounds(55, 32, 70, 14);
		this.add(lblAmount);

		JLabel lblDate = new JLabel("Inactive?");
		lblDate.setFont(new Font("Tahoma", Font.BOLD, 11));
		lblDate.setBounds(230, 32, 69, 14);
		this.add(lblDate);

		btnRefresh = new JButton("Refresh");
		btnRefresh.addActionListener(new RefreshUsers());
		btnRefresh.setBounds(189, 408, 110, 25);
		this.add(btnRefresh);

		feedbackLabel.setLocation(0, 255);

		lblUserId = new JLabel("No Users");
		lblUserId.setFont(new Font("Tahoma", Font.PLAIN, 15));

		lblUserId.setBounds(5, 292, 170, 19);
		this.add(lblUserId);

		txtpnTextFromTransaction = new JTextPane();
		txtpnTextFromTransaction.setFont(new Font("Tahoma", Font.PLAIN, 12));
		txtpnTextFromTransaction.setBackground(SystemColor.control);
		txtpnTextFromTransaction.setText("");
		txtpnTextFromTransaction.setEditable(false);
		txtpnTextFromTransaction.setBounds(5, 322, 294, 75);
		this.add(txtpnTextFromTransaction);

		loadMoreButton = new JButton("Load more");
		loadMoreButton.setBounds(189, 291, 110, 25);
		loadMoreButton.addActionListener(new LoadMoreUsers());
		loadMoreButton.setEnabled(false);
		add(loadMoreButton);

		lblSearch = new JLabel("Search");
		lblSearch.setFont(new Font("Tahoma", Font.PLAIN, 15));
		lblSearch.setBounds(5, 201, 170, 19);
		add(lblSearch);

		searchField = new JTextField();
		searchField.setBounds(53, 231, 199, 25);
		searchField.getDocument().addDocumentListener(new SearchUsers());
		add(searchField);
		searchField.setColumns(10);

		getUserButton = new JButton("Get User");
		getUserButton.setBounds(189, 199, 111, 25);
		getUserButton.addActionListener(new GetUser());
		add(getUserButton);

		gotUsersUntil = 2147483647;
	}

	private class RefreshUsers implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			refreshUsers();
		}
	}

	private class LoadMoreUsers implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			loadMoreUsers();
		}
	}

	private class GetUser implements ActionListener {
		public void actionPerformed(ActionEvent arg0) {
			getUser();
		}
	}

	/**
	 * Scrolls the JTable to the top of the list.
	 */
	public void scrollToTop() {
		scrollpane.getVerticalScrollBar().setValue(0);
		userTable.setRowSelectionInterval(0, 0);
	}

	/**
	 * Enables or disables the loadMoreButton based on the value specified.
	 * 
	 * @param value
	 *            If true, enables the button, if false, disables the button.
	 */
	public void enableLoadMoreButton(boolean value) {
		if (value) {
			loadMoreButton.setEnabled(true);
		} else {
			loadMoreButton.setEnabled(false);
		}

	}

	/**
	 * Enables or disables the refreshButton based on the value specified.
	 * 
	 * @param value
	 *            If true, enables the button, if false, disables the button.
	 */
	public void enableRefreshButton(boolean value) {
		if (value) {
			btnRefresh.setEnabled(true);
		} else {
			btnRefresh.setEnabled(false);
		}
	}

	/**
	 * Sets the user text of the userID field to the supplied value.
	 * 
	 * @param id
	 *            The ID to input in the text field.
	 */
	public void setUserID(String id) {
		lblUserId.setText("User #" + id);
	}

	/**
	 * Switches to the EditUserPanel with the selected user a argument. Used
	 * when admin presses the Edit User button.
	 */
	public void getUser() {
		UI.MainFrame.switchToPanel(UI.MainFrame.edit_user, selectedUser);

	}

	/**
	 * Sets the text pane containing the transaction description to the value
	 * specified.
	 * 
	 * @param text
	 *            The text to set as description.
	 */
	public void setUserText(String text) {
		txtpnTextFromTransaction.setText(text);
	}

	/**
	 * Clears all the fields, resets the labels and buttons, clears the JTable
	 * and sets the user tracking variable to the maximum allowed for integers.
	 */
	@Override
	public void clearFields() {
		feedbackLabel.setText("");
		lblUserId.setText("No users");
		loadMoreButton.setEnabled(true);
		btnRefresh.setEnabled(true);
		gotUsersUntil = 2147483647;
		tablemodel.clearTable();
		searchField.setText("");
	}

	/**
	 * Creates a new Runnable of the type LoadMoreUsersRunnable, sets it to the
	 * current runnable for the panel, places it in its own thread and runs it.
	 */
	public void loadMoreUsers() {
		runnable = new LoadMoreUsersRunnable();
		Thread t = new Thread(runnable);
		t.start();
	}

	/**
	 * Creates a new Runnable of the type RefreshUsersRunnable, sets it to the
	 * current runnable for the panel, places it in its own thread and runs it.
	 */
	public void refreshUsers() {
		runnable = new RefreshUsersRunnable();
		Thread t = new Thread(runnable);
		t.start();
	}

	/**
	 * Sets the selectedUser variable to a new Account object matching the row
	 * selected in the JTable.
	 * 
	 * @param row
	 *            The row to fetch info for - in this case this is always the
	 *            clicked row.
	 */

	public void selectUser(int row) {
		if (tablemodel.getRowCount() > 0) {
			Object deactivatedString = "";
			Object objid = tablemodel.getValueAt(row, 0);
			Object objemail = tablemodel.getValueAt(row, 1);
			Object objdeactivated = tablemodel.getValueAt(row, 2);
			Object objbalance = tablemodel.getValueAt(row, 3);
			Object objfirstname = tablemodel.getValueAt(row, 4);
			Object objlastname = tablemodel.getValueAt(row, 5);

			if (objdeactivated.toString().equalsIgnoreCase("true")) {
				deactivatedString = "No";
			} else {
				deactivatedString = "Yes";
			}
			setUserID(objid.toString());
			setUserText(objemail.toString() + "\nCurrent balance: "
					+ objbalance.toString() + "\nActive: "
					+ deactivatedString.toString());
			selectedUser = new Account(objemail.toString(),
					Integer.valueOf(objid.toString()),
					Double.valueOf(objbalance.toString()),
					objfirstname.toString(), objlastname.toString(), false,
					Boolean.valueOf(objdeactivated.toString()));
		}
	}

	/**
	 * Creates a new Runnable of the type RefreshUsersRunnable, sets it to the
	 * current runnable for the panel, places it in its own thread and runs it.
	 * Additionally this method tells the previously initiated thread to stop
	 * whatever it's doing first.
	 */
	public void searchUsers() {
		stopThread();
		runnable = new SearchForUsersRunnable();
		Thread t = new Thread(runnable);
		t.start();
	}

	/**
	 * See the run-method of this class for details.
	 * 
	 */
	private class LoadMoreUsersRunnable extends BaseRunnableBCBS {
		/**
		 * Loads additional users based on the gotUsersUntil variable. When this
		 * variable was previously set to the ID of the last transaction
		 * fetched, it can be used to only fetch additional users without mixing
		 * the results. When more users have been fetched, the gotUsersUntil
		 * variable is again set to the ID of the last user fetched.
		 */
		@Override
		public void run() {
			SQLReader reader = new SQLReader();
			enableRefreshButton(false);
			enableLoadMoreButton(false);
			feedback("Loading...", true);
			try {
				if (!stop) {
					int lowestid = tablemodel.addData(reader
							.getUsers(gotUsersUntil));
					if (!stop) {
						gotUsersUntil = lowestid;
					}
				}
				if (!stop) {
					if (gotUsersUntil == -1) {
						enableLoadMoreButton(false);
					} else {
						enableLoadMoreButton(true);
					}
					enableRefreshButton(true);
					clearFeedback();
					selectUser(0);
				}
			} catch (SQLException e) {
				if (!stop) {
					feedback("Connection Error! Try again!", false);
					enableRefreshButton(true);
				}

			}
		}
	}

	/**
	 * See the run-method of this class for details.
	 * 
	 */
	private class RefreshUsersRunnable extends BaseRunnableBCBS {
		/**
		 * Refreshes the list of users by calling refreshData from the
		 * tablemodel. Buttons are properly disabled while the method runs. It
		 * also determines if more users can be fetched by setting the
		 * gotUsersUntil variable.
		 * 
		 */
		@Override
		public void run() {

			SQLReader reader = new SQLReader();
			feedback("Loading....", true);
			enableRefreshButton(false);
			enableLoadMoreButton(false);
			try {
				if (!stop) {
					int lowestid = tablemodel.refreshData(reader
							.getUsers(2147483647));
					if (!stop) {
						gotUsersUntil = lowestid;
					}
				}

				if (!stop) {
					if (gotUsersUntil == -1) {
						enableLoadMoreButton(false);
					} else {
						enableLoadMoreButton(true);

					}
					scrollToTop();
					selectUser(0);
					enableRefreshButton(true);
					clearFeedback();
				}
			} catch (SQLException e) {
				if (!stop) {
					feedback("Connection Error! Try again!", false);
					enableRefreshButton(true);
				}
			}
		}
	}

	/**
	 * See the run-method of this class for details.
	 * 
	 */
	private class SearchForUsersRunnable extends BaseRunnableBCBS {
		/**
		 * If this runnable has not been cancelled (if its stop-method has not
		 * been invoked) before 600ms, it searches for users in the database
		 * according to input; if text field is empty, it refreshes the user
		 * list entirely.
		 * 
		 */
		@Override
		public void run() {
			try {
				Thread.sleep(600);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			if (!stop) {
				if (!searchTerm.isEmpty()) {
					loadMoreButton.setEnabled(false);
					SQLReader reader = new SQLReader();
					feedback("Loading", true);

					try {
						if (!stop) {
							ArrayList<Account> list = new ArrayList<Account>();
							list = reader.getSpecificUsers(searchTerm);
							if (!stop) {
								if (list.isEmpty()) {
									tablemodel.clearTable();
									clearFeedback();
								} else {
									tablemodel.refreshData(list);
									clearFeedback();
								}
							}

						}

					} catch (SQLException e) {
						if (!stop) {
							feedback("Connection error!", false);
						}
					}
				} else if (!stop) {
					refreshUsers();
				}
			}
		}

	}
	/**
	 * Listens for keyboard input and searches for accounts when the user types in the search criteria.
	 * 
	 *
	 */
	private class SearchUsers implements DocumentListener {

		@Override
		public void insertUpdate(DocumentEvent e) {
			searchTerm = searchField.getText().trim();
			searchUsers();
		}

		@Override
		public void removeUpdate(DocumentEvent e) {
			searchTerm = searchField.getText().trim();
			searchUsers();
		}

		@Override
		public void changedUpdate(DocumentEvent e) {
			searchTerm = searchField.getText().trim();
			searchUsers();
		}
	}
}
